﻿namespace ClassyBlog.Infrastructure.Specs.Unit
{
    using System;
    using System.IO;
    using System.Net;
    using System.Threading;

    using Machine.Specifications;
    using NSubstitute;

    [Subject(typeof(Http))]
    public class when_creating_request
    {
        static HttpWebRequest requst;

        Because of = () => requst =
            Http.CreateRequest(
            new Uri("http://kazimanzurrashid.com")) as HttpWebRequest;

        It should_return_http_web_request = () => requst.ShouldNotBeNull();
    }

    [Subject(typeof(Http))]
    public class when_trying_to_get_content_with_null_url
    {
        static Http http;
        static Exception exception;

        Establish context = () => http = new Http();

        Because of = () => exception =
            Catch.Exception(() => 
                http.GetAsync(
                null,
                (e, s, t, c) =>
                    {
                    }));

        It should_throw_exception = () =>
            exception.ShouldBeOfType<ArgumentNullException>();
    }

    [Subject(typeof(Http))]
    public class when_trying_to_get_content_with_null_callback
    {
        static Http http;
        static Exception exception;

        Establish context = () => http = new Http();

        Because of = () => exception =
            Catch.Exception(() =>
                http.GetAsync(new Uri("http://kazimanzurrashid.com"), null));

        It should_throw_exception = () =>
            exception.ShouldBeOfType<ArgumentNullException>();
    }

    [Subject(typeof(Http))]
    public class when_getting_content
    {
        static Stream stream;
        static Http http;
        static bool exception;

        Establish context = () =>
        {
            stream = new MemoryStream();
            var writer = new StreamWriter(stream);
            writer.Write(new string('x', 8096 * 3));
            writer.Flush();
            stream.Seek(0, SeekOrigin.Begin);

            var response = Substitute.For<WebResponse>();
            response.GetResponseStream().Returns(stream);

            var request = Substitute.For<WebRequest>();

            request.When(r => r.BeginGetResponse(
                Arg.Any<AsyncCallback>(),
                Arg.Any<object>())).Do(info =>
                  {
                      var args = info.Args();
                      var callback = (AsyncCallback)args[0];
                      var state = args[1];

                      var asyncResult = Substitute.For<IAsyncResult>();

                      asyncResult.AsyncState.Returns(state);

                      callback(asyncResult);
                  });

            request.EndGetResponse(Arg.Any<IAsyncResult>()).Returns(response);

            Http.CreateRequest = url => request;

            http = new Http();
        };

        Because of = () =>
        {
            using (var wait = new AutoResetEvent(false))
            {
                http.GetAsync(
                    new Uri("http://kazimanzurrashid.com"),
                    (error, statusCode, contentType, content) =>
                    {
                        exception = error;
                        wait.Set();
                    });

                 wait.WaitOne();
            }
        };

        It should_not_be_an_exception = () => exception.ShouldBeFalse();

        Cleanup on_exit = () =>
        {
            stream.Close();
            Http.CreateRequest = null;
        };
    }

    [Subject(typeof(Http))]
    public class when_exception_occurs_while_getting_content
    {
        static Http http;
        static bool exception;

        Establish context = () =>
        {
            var request = Substitute.For<WebRequest>();

            request.When(r => r.BeginGetResponse(
                Arg.Any<AsyncCallback>(),
                Arg.Any<object>())).Do(info =>
                  {
                      var args = info.Args();
                      var callback = (AsyncCallback)args[0];
                      var state = args[1];

                      var asyncResult = Substitute.For<IAsyncResult>();

                      asyncResult.AsyncState.Returns(state);

                      callback(asyncResult);
                  });

            request.EndGetResponse(Arg.Any<IAsyncResult>())
                   .Returns(info => { throw new WebException(); });

            Http.CreateRequest = url => request;

            http = new Http();
        };

        Because of = () =>
        {
            using (var wait = new AutoResetEvent(false))
            {
                http.GetAsync(
                    new Uri("http://kazimanzurrashid.com"),
                    (error, statusCode, contentType, content) =>
                    {
                        exception = error;
                        wait.Set();
                    });

                 wait.WaitOne();
            }
        };

        It should_be_an_exception = () => exception.ShouldBeTrue();

        Cleanup on_exit = () => Http.CreateRequest = null;
    }
}